/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: spi_pol_mpc83xx.c$
* $Version : 3.8.2.1$
* $Date    : Feb-13-2012$
*
* Comments:
*    
*    This file contains the polled functions for the SPI device 
*    low level functions for the mpc83xx.
*
*END*********************************************************************/

#include "mqx_inc.h"
#include "bsp.h"
#include "bsp_prv.h"
//#include "io_prv.h"
#include "fio_prv.h"
#include "timer.h"

#include "spi_pol_prv.h"
#include "io_gpio.h"

extern _mqx_uint _mpc83xx_spi_polled_init(MPC83xx_SPI_INIT_STRUCT_PTR, pointer _PTR_, char _PTR_);
extern _mqx_uint _mpc83xx_spi_polled_deinit(MPC83xx_SPI_INIT_STRUCT_PTR, MPC83xx_SPI_INFO_STRUCT_PTR);
extern _mqx_uint _mpc83xx_spi_polled_ioctl(MPC83xx_SPI_INFO_STRUCT_PTR, uint_32, uint_32_ptr, uint_32);

extern _mqx_uint _mpc83xx_spi_polled_rx(IO_SPI_POLLED_DEVICE_STRUCT_PTR,  char _PTR_, _mqx_int);
extern _mqx_uint _mpc83xx_spi_polled_tx(IO_SPI_POLLED_DEVICE_STRUCT_PTR, char _PTR_, _mqx_int);
extern void      _bsp_spi_io_init(MPC83xx_SPI_INFO_STRUCT_PTR);
extern uint_32   _mpc83xx_spi_polled_tx_rx (MPC83xx_SPI_INFO_STRUCT_PTR, char_ptr, boolean, _mqx_int);

uint_32 _bsp_spi_clear_cs(void);
uint_32 _bsp_spi_set_cs(void);


/*FUNCTION*-------------------------------------------------------------------
* 
* Function Name    : _mpc83xx_spi_polled_install
* Returned Value   : uint_32 a task error code or MQX_OK
* Comments         :
*    Install an SPI device.
*
*END*----------------------------------------------------------------------*/

uint_32 _mpc83xx_spi_polled_install
   (
      /* [IN] A string that identifies the device for fopen */
      char_ptr           identifier,
  
      /* [IN] The I/O init data pointer */
      pointer            init_data_ptr
   )
{ /* Body */

	return _io_spi_polled_install(identifier,
	(uint_32 (_CODE_PTR_)(pointer, pointer _PTR_, char_ptr))_mpc83xx_spi_polled_init,
	(uint_32 (_CODE_PTR_)(pointer, pointer))_mpc83xx_spi_polled_deinit,
	(_mqx_int (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_spi_polled_rx,
	(_mqx_int (_CODE_PTR_)(pointer, char_ptr, int_32))_mpc83xx_spi_polled_tx,
	(_mqx_int (_CODE_PTR_)(pointer, uint_32, _mqx_uint_ptr, uint_32))_mpc83xx_spi_polled_ioctl, 
	(pointer)init_data_ptr);
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_init
* Returned Value   : MQX_OK or a mqx error code
* Comments         :
*    This function initializes the spi serial polled input and output.
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_polled_init
   (
      /* [IN] the initialization information for the device being opened */
      MPC83xx_SPI_INIT_STRUCT_PTR    io_init_ptr,

      /* [OUT] the address to store device specific information */
      pointer _PTR_                  io_info_ptr_ptr,
       
      /* [IN] the rest of the name of the device opened */
      char_ptr                       open_name_ptr
   )
{ /* Body */
   KERNEL_DATA_STRUCT_PTR                 kernel_data;
   PSP_SUPPORT_STRUCT_PTR                 support_ptr;
   MPC83xx_SPI_STRUCT_PTR                 spi_ptr;
   MPC83xx_SPI_INFO_STRUCT_PTR            io_info_ptr;
   uint_32                                div;
   uint_32                                result;     
   uint_32                                immr;
      
   immr = (uint_32)BSP_IMMR_BASE;

   _GET_KERNEL_DATA(kernel_data);
   
   support_ptr = kernel_data->PSP_SUPPORT_PTR;
  
   spi_ptr  = (MPC83xx_SPI_STRUCT_PTR)(immr + MPC83xx_SPI_BASE);
 
   //io_init_ptr = io_dev_ptr->DEV_INIT_DATA_PTR;
   io_info_ptr = (MPC83xx_SPI_INFO_STRUCT_PTR)_mem_alloc_system_zero(
                  (uint_32)sizeof(MPC83xx_SPI_INFO_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
   if (io_info_ptr == NULL) {
      return(MQX_OUT_OF_MEMORY);
   } /* Endif */
#endif

   *io_info_ptr_ptr = io_info_ptr;

   io_info_ptr->DEV_REG_PTR = (pointer)spi_ptr;
   io_info_ptr->INIT        = *io_init_ptr;
   io_info_ptr->CS_ENABLED  = TRUE;
  
   /* Disable all SPI interrupts */
   spi_ptr->IMASK = 0x00;

   /* Clear out all SPI events */
   spi_ptr->IEVENT = MPC83xx_SPI_IEVENT_LT | MPC83xx_SPI_IEVENT_DNR |
                     MPC83xx_SPI_IEVENT_OV | MPC83xx_SPI_IEVENT_UN  |
                     MPC83xx_SPI_IEVENT_MME | MPC83xx_SPI_IEVENT_NE |
                     MPC83xx_SPI_IEVENT_NF;
  
   /* Disable and clear SPI before initializing it */
   spi_ptr->MR  = 0;      

//   io_dev_ptr->FLAGS |= SPI_FLAG_FULL_DUPLEX;
   
   /* Select operation mode */
   switch(io_init_ptr->TRANSFER_MODE)
   {
      case(SPI_DEVICE_LPBACK_MODE):
         /* Loopback mode */
         spi_ptr->MR = MPC83xx_SPI_MR_LOOP;
         break;
      case(SPI_DEVICE_MASTER_MODE):
         /* Master mode */
         spi_ptr->MR = MPC83xx_SPI_MR_MS;
         
	     break;
      case(SPI_DEVICE_SLAVE_MODE):
         break;
      default:
         return (SPI_ERROR_MODE_INVALID);
   } /* Endswitch */
   
   /* 
   ** There are four possiable combinations for seting up 
   ** SPI clock polarity and phase. 
   */
   switch(io_init_ptr->CLOCK_POL_PHASE)
   {
      case(SPI_CLK_POL_PHA_MODE0):
	     /* Inactive state of SPICLK is low */
	     spi_ptr->MR &= ~(MPC83xx_SPI_MR_CI);
	     /* SPICLK transitions middle of bit timing */
	     spi_ptr->MR &= ~(MPC83xx_SPI_MR_CP);
         break;
      case(SPI_CLK_POL_PHA_MODE1):
    	 /* Inactive state of SPICLK is low */
	     spi_ptr->MR &= ~(MPC83xx_SPI_MR_CI);
	     /* SPICLK transitions begining of bit timing */
	     spi_ptr->MR |= MPC83xx_SPI_MR_CP;
         break;
      case(SPI_CLK_POL_PHA_MODE2):
	     /* Inactive state of SPICLK is high */
	     spi_ptr->MR |= MPC83xx_SPI_MR_CI;
	     /* SPICLK transitions begining of bit timing */
	     spi_ptr->MR |= MPC83xx_SPI_MR_CP;
         break;
      case(SPI_CLK_POL_PHA_MODE3):
	     /* Inactive state of SPICLK is low */
	     spi_ptr->MR |= MPC83xx_SPI_MR_CI;
	     /* SPICLK transitions middle of bit timing */
	     spi_ptr->MR &= ~(MPC83xx_SPI_MR_CP);
         break;
      default:
         return (SPI_ERROR_TRANSFER_MODE_INVALID);
   } /* Endswitch */
   
   /* Set Data transfer Endian */
   if (io_init_ptr->ENDIAN == SPI_DEVICE_BIG_ENDIAN) {
      if((io_init_ptr->CHAR_LEN == 8) || (io_init_ptr->CHAR_LEN == 16) ||
         (io_init_ptr->CHAR_LEN == 32)) {
         spi_ptr->MR |= MPC83xx_SPI_MR_REV;	
      }
      else {
      	return(SPI_ERROR_ENDIAN_INVALID);
      }
      
   } /* Endif */

   if((io_init_ptr->CHAR_LEN > 3) && (io_init_ptr->CHAR_LEN <= 16)) {
      spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN_MASK;
      spi_ptr->MR |= ((io_init_ptr->CHAR_LEN - 1) << 20) & MPC83xx_SPI_MR_LEN_MASK;
   }
   else if(io_init_ptr->CHAR_LEN == 32) {
   	  spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN_MASK;
   }
   else {
   	  return(SPI_ERROR_TRANSFER_SIZE_INVALID);
   } /* Endif */

   /* Set Baud rate */
   div = support_ptr->CSBCLK / io_init_ptr->BAUD_RATE;
   if((div == 0) || (div > 1024)) {
   	  return(SPI_ERROR_BAUD_RATE_INVALID);
   } /* Endif */
   
   if(div > 64) {
      spi_ptr->MR |= MPC83xx_SPI_MR_DIV16;
   
      if(div & 0x3F) {
   	     div /= 64;
   	  }
   	  else {
   	     div = (div / 64) - 1;
   	  } /* Endif */
   	  
   	  io_init_ptr->BAUD_RATE = support_ptr->CSBCLK / (64 * (div + 1));
   }
   else {
      spi_ptr->MR &= ~MPC83xx_SPI_MR_DIV16;
   	  if(div & 0x03) {
   	     div /= 4;
   	  }
   	  else {
   	    div = (div / 4) - 1;
   	  } /* Endif */
   	  
      io_init_ptr->BAUD_RATE = support_ptr->CSBCLK / (4 * (div + 1));
   } /* Endif */
   
   spi_ptr->MR &= ~MPC83xx_SPI_MR_PRESC_MASK;
   spi_ptr->MR |= (div << 16) & MPC83xx_SPI_MR_PRESC_MASK;
   
   if(io_init_ptr->OPEN_DRAIN == TRUE) {
   	  spi_ptr->MR |= MPC83xx_SPI_MR_OD;
   } /* Endif */
   
   /* Board specific SPI initialization */
   // Setting GPIO pins(Slave chip Select) etc 
   // should be taken care in this function 
   _bsp_spi_io_init(io_info_ptr);
 
   /* Enable SPI */
   spi_ptr->MR |= MPC83xx_SPI_MR_EN;
      
   return (MQX_OK);
   
} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_deinit
* Returned Value   : MQX_OK or a mqx error code
* Comments         :
*    This function de-initializes SPI
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_polled_deinit
   (
      /* [IN] the initialization information for the device being opened */
      MPC83xx_SPI_INIT_STRUCT_PTR io_init_ptr,

      /* [IN] the address of the device specific information */
      MPC83xx_SPI_INFO_STRUCT_PTR io_info_ptr
   )
{ /* Body */
   MPC83xx_SPI_STRUCT_PTR     spi_ptr;
   
   spi_ptr = io_info_ptr->DEV_REG_PTR;

   /* wait till it finishes the ongoing transmission */
   while(!(spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NF));
   
   /* Disable and clear SPI controller */
   spi_ptr->MR = 0x00;   
   
   /* Clear out all SPI events */
   spi_ptr->IEVENT = MPC83xx_SPI_IEVENT_LT | MPC83xx_SPI_IEVENT_DNR |
                     MPC83xx_SPI_IEVENT_OV | MPC83xx_SPI_IEVENT_UN  |
                     MPC83xx_SPI_IEVENT_MME | MPC83xx_SPI_IEVENT_NE |
                     MPC83xx_SPI_IEVENT_NF;
   
   _mem_free(io_info_ptr);

   return(MQX_OK);

} /* Endbody */

 
/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_ioctl
* Returned Value   : uint_32 MQX_OK or a mqx error code.
* Comments         : 
*    This function performs miscellaneous services for
*    the SPI I/O device.  
*
*END*********************************************************************/

uint_32 _mpc83xx_spi_polled_ioctl
   (
      /* [IN] the address of the device specific information */
      MPC83xx_SPI_INFO_STRUCT_PTR io_info_ptr,

      /* [IN] The command to perform */
      uint_32                    cmd,

      /* [IN] Parameters for the command */
      uint_32_ptr                param_ptr,

      /* [IN] Opening flags */
      uint_32                    flags
   )
{ /* Body */
   KERNEL_DATA_STRUCT_PTR                 kernel_data;
   PSP_SUPPORT_STRUCT_PTR                 support_ptr;
   MPC83xx_SPI_STRUCT_PTR                 spi_ptr;
   SPI_CS_CALLBACK_STRUCT_PTR             callbacks;
   uint_32                                div;
   uint_32                                result = MQX_OK;     
   uint_32                                immr;
   uint_32                        val, num, size, i;
   SPI_READ_WRITE_STRUCT_PTR      rw_ptr;
   char_ptr                      input, output;
   
   _PSP_GET_MBAR(immr);
   _GET_KERNEL_DATA(kernel_data);
   
   support_ptr = kernel_data->PSP_SUPPORT_PTR;
   
   spi_ptr  = io_info_ptr->DEV_REG_PTR;

   switch (cmd) {
      case IO_IOCTL_SPI_GET_BAUD:
         *param_ptr = io_info_ptr->INIT.BAUD_RATE;
         break;
      case IO_IOCTL_SPI_SET_BAUD:
         io_info_ptr->INIT.BAUD_RATE = *param_ptr;
         
         /* Disable SPI */
         spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;

         div = support_ptr->CSBCLK / (*param_ptr);
         if((div == 0) || (div > 1024)) {
             div = 1023;
         } /* Endif */
   
         if(div > 64) {
            spi_ptr->MR |= MPC83xx_SPI_MR_DIV16;
   
            if(div & 0x3F) {
   	           div /= 64;
   	        }
   	        else {
   	           div = (div / 64) - 1;
   	        } /* Endif */
   	  
   	        io_info_ptr->INIT.BAUD_RATE = support_ptr->CSBCLK / (64 * (div + 1));
         }
         else {
              spi_ptr->MR &= ~MPC83xx_SPI_MR_DIV16;
              
   	        if(div & 0x03) {
   	           div /= 4;
   	        }
   	        else {
   	          div = (div / 4) - 1;
   	        } /* Endif */
   	  
   	        io_info_ptr->INIT.BAUD_RATE = support_ptr->CSBCLK / (4 * (div + 1));
         } /* Endif */
   
         spi_ptr->MR &= ~MPC83xx_SPI_MR_PRESC_MASK;
         spi_ptr->MR |= (div << 16) & MPC83xx_SPI_MR_PRESC_MASK;

         /* Enable SPI */
         spi_ptr->MR |= MPC83xx_SPI_MR_EN;
         break;

   	  case IO_IOCTL_SPI_GET_TRANSFER_MODE:
         *param_ptr = io_info_ptr->INIT.TRANSFER_MODE;
   	     break;

   	  case IO_IOCTL_SPI_SET_TRANSFER_MODE:
        spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;   	  
         switch ( *param_ptr ) {
            case(SPI_DEVICE_SLAVE_MODE):
	           spi_ptr->MR &= ~MPC83xx_SPI_MR_MS;
	           io_info_ptr->INIT.TRANSFER_MODE = SPI_DEVICE_SLAVE_MODE;
               break;
            case(SPI_DEVICE_MASTER_MODE):
	           spi_ptr->MR |= MPC83xx_SPI_MR_MS;
	           io_info_ptr->INIT.TRANSFER_MODE = SPI_DEVICE_MASTER_MODE;
               break;
            case(SPI_DEVICE_LPBACK_MODE):
	           spi_ptr->MR |= MPC83xx_SPI_MR_LOOP;
	           io_info_ptr->INIT.TRANSFER_MODE = SPI_DEVICE_LPBACK_MODE;
               break;
            default:
               result = SPI_ERROR_MODE_INVALID;
         } /* Endswitch */
         spi_ptr->MR |= MPC83xx_SPI_MR_EN;
   	     break;

   	  case IO_IOCTL_SPI_GET_MODE:
         *param_ptr = io_info_ptr->INIT.CLOCK_POL_PHASE;
   	     break;

   	  case IO_IOCTL_SPI_SET_MODE:
         spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;   	  
         switch ( *param_ptr ) {
            case(SPI_CLK_POL_PHA_MODE0):
	           /* Inactive state of SPICLK is low */
	           spi_ptr->MR &= ~(MPC83xx_SPI_MR_CI);
	           /* SPICLK transitions middle of bit timing */
	           spi_ptr->MR &= ~(MPC83xx_SPI_MR_CP);
	           io_info_ptr->INIT.CLOCK_POL_PHASE = SPI_CLK_POL_PHA_MODE0;
               break;
            case(SPI_CLK_POL_PHA_MODE1):
	           /* Inactive state of SPICLK is low */
	           spi_ptr->MR &= ~(MPC83xx_SPI_MR_CI);
	           /* SPICLK transitions begining of bit timing */
	           spi_ptr->MR |= MPC83xx_SPI_MR_CP;
	           io_info_ptr->INIT.CLOCK_POL_PHASE = SPI_CLK_POL_PHA_MODE1;
               break;
            case(SPI_CLK_POL_PHA_MODE2):
	           /* Inactive state of SPICLK is high */
	           spi_ptr->MR |= MPC83xx_SPI_MR_CI;
	           /* SPICLK transitions begining of bit timing */
	           spi_ptr->MR |= MPC83xx_SPI_MR_CP;
	           io_info_ptr->INIT.CLOCK_POL_PHASE = SPI_CLK_POL_PHA_MODE2;
               break;
            case(SPI_CLK_POL_PHA_MODE3):
	           /* Inactive state of SPICLK is low */
	           spi_ptr->MR |= MPC83xx_SPI_MR_CI;
	           /* SPICLK transitions middle of bit timing */
	           spi_ptr->MR &= ~(MPC83xx_SPI_MR_CP);
	           io_info_ptr->INIT.CLOCK_POL_PHASE = SPI_CLK_POL_PHA_MODE3;
               break;
            default:
               result = SPI_ERROR_TRANSFER_MODE_INVALID;
         } /* Endswitch */
         spi_ptr->MR |= MPC83xx_SPI_MR_EN;
   	     break;

     case IO_IOCTL_SPI_ENABLE_MODF:
        result = MQX_IO_OPERATION_NOT_AVAILABLE;
        break;

     case IO_IOCTL_SPI_DISABLE_MODF:
        break;

     case IO_IOCTL_FLUSH_OUTPUT:
         while ((0 != io_info_ptr->RECEIVING) || (0 != io_info_ptr->ONTHEWAY) || (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NE) || !(spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NF)) 
            {};  /* Waiting for completion */
         if ((0 == (flags & SPI_FLAG_NO_DEASSERT_ON_FLUSH)) || (IO_IOCTL_SPI_FLUSH_DEASSERT_CS == cmd)) {
            /* Deassert chip select */
            if (io_info_ptr->CS_CALLBACK && io_info_ptr->CS_ENABLED) {
               io_info_ptr->CS_CALLBACK(io_info_ptr->INIT.CS, TRUE, io_info_ptr->CS_USERDATA);
            }                
         }
         break;
            
     case IO_IOCTL_SPI_FLUSH_DEASSERT_CS:
         while ((0 != io_info_ptr->RECEIVING) || (0 != io_info_ptr->ONTHEWAY) || (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NE) || !(spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NF)) 
            {};  /* Waiting for completion */
         /* Force deassert chip select */
         if (io_info_ptr->CS_CALLBACK && io_info_ptr->CS_ENABLED) {
            io_info_ptr->CS_CALLBACK(io_info_ptr->INIT.CS, TRUE, io_info_ptr->CS_USERDATA);
         }                
         break;
            
     case IO_IOCTL_SPI_GET_FRAMESIZE:
         div = (spi_ptr->MR & MPC83xx_SPI_MR_LEN_MASK) >> 20;
         if (div == 0) {
         	*param_ptr = 32;
         }
         else {
         	*param_ptr = div + 1;
         }
         break;

     case IO_IOCTL_SPI_SET_FRAMESIZE:
         if (NULL == param_ptr) {
            result = SPI_ERROR_INVALID_PARAMETER;
         } 
         else  {
            switch ( *param_ptr ) {
               case 8:
                  io_info_ptr->INIT.CHAR_LEN = 8;
	              spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN(0xF);             
	              spi_ptr->MR |= MPC83xx_SPI_MR_LEN(7);                                            
   	              break;
               case 16:
                  io_info_ptr->INIT.CHAR_LEN = 16;
	              spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN(0xF);             
	              spi_ptr->MR |= MPC83xx_SPI_MR_LEN(15);                                            
   	              break;
               case 32:
                  io_info_ptr->INIT.CHAR_LEN = 32;
	              spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN(0xF);             
   	              break;
               default:
                  result = MQX_IO_OPERATION_NOT_AVAILABLE;
            }
         }
         break;

     /* seems used to enable/disable CS in original examples/code */
     case IO_IOCTL_SPI_GET_CS:
         *param_ptr = io_info_ptr->CS_ENABLED;
         break;
         
      case IO_IOCTL_SPI_SET_CS:
         if (NULL == param_ptr) {
            result = SPI_ERROR_INVALID_PARAMETER;
         } 
         else {
            io_info_ptr->CS_ENABLED = *param_ptr;
         }
         break;
         
      case IO_IOCTL_SPI_SET_CS_CALLBACK:
         if (NULL == param_ptr) {
            result = SPI_ERROR_INVALID_PARAMETER;
         } 
         else {
            callbacks = (SPI_CS_CALLBACK_STRUCT_PTR)(param_ptr);
            io_info_ptr->CS_CALLBACK = callbacks->CALLBACK;
            io_info_ptr->CS_USERDATA = callbacks->USERDATA;
         }
         break;
         
   	  case IO_IOCTL_SPI_GET_ENDIAN:
   	     *param_ptr = io_info_ptr->INIT.ENDIAN;
   	     break;
   	     
   	  case IO_IOCTL_SPI_SET_ENDIAN:
         if (NULL == param_ptr) {
            result = SPI_ERROR_INVALID_PARAMETER;
   	        break;
         } 

   	     spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;
         if (*param_ptr == SPI_DEVICE_BIG_ENDIAN) {
            if((io_info_ptr->INIT.CHAR_LEN == 8) || 
               (io_info_ptr->INIT.CHAR_LEN == 16) ||
               (io_info_ptr->INIT.CHAR_LEN == 32)) {
               spi_ptr->MR |= MPC83xx_SPI_MR_REV;	
            }
            else {
      	      result = SPI_ERROR_ENDIAN_INVALID;
      	      spi_ptr->MR |= MPC83xx_SPI_MR_EN;
      	      break;
            }
         }
         else {
            spi_ptr->MR &= ~MPC83xx_SPI_MR_REV;		
         }/* Endif */
         io_info_ptr->INIT.ENDIAN = *param_ptr;
         spi_ptr->MR |= MPC83xx_SPI_MR_EN;
         break;

   	  case IO_IOCTL_SPI_SET_TRANSFER_SIZE:
   	     spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;
         if((*param_ptr > 3) && (*param_ptr <= 16)) {
            if((io_info_ptr->INIT.ENDIAN == SPI_DEVICE_BIG_ENDIAN) && 
               (!((*param_ptr == 8) || (*param_ptr == 16)))) {
            	
               result = SPI_ERROR_INVALID_PARAMETER;
               spi_ptr->MR |= MPC83xx_SPI_MR_EN;
               break;
            } /* Endif */
            spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN_MASK;
            spi_ptr->MR |= ((*param_ptr - 1) << 20) & MPC83xx_SPI_MR_LEN_MASK;
            io_info_ptr->INIT.CHAR_LEN = *param_ptr;
         }
         else if(*param_ptr == 32) {
   	        spi_ptr->MR &= ~MPC83xx_SPI_MR_LEN_MASK;
            io_info_ptr->INIT.CHAR_LEN = *param_ptr;
         }
         else {
   	        result = SPI_ERROR_INVALID_PARAMETER;
         } /* Endif */
         spi_ptr->MR |= MPC83xx_SPI_MR_EN;
		 break;
 
   	  case IO_IOCTL_SPI_GET_TRANSFER_SIZE:
         *param_ptr = io_info_ptr->INIT.CHAR_LEN;
         break;
         
   	  case IO_IOCTL_SPI_DEVICE_ENABLE:
   	     spi_ptr->MR |= MPC83xx_SPI_MR_EN;
   	     break;

   	  case IO_IOCTL_SPI_DEVICE_DISABLE:
   	     spi_ptr->MR &= ~MPC83xx_SPI_MR_EN;
   	     break;
 
      case IO_IOCTL_SPI_GET_STATS:
         *((SPI_STATISTICS_STRUCT_PTR)param_ptr) = io_info_ptr->STATS;
         break;

      case IO_IOCTL_SPI_CLEAR_STATS:
         io_info_ptr->STATS.INTERRUPTS   = 0;
         io_info_ptr->STATS.RX_PACKETS   = 0;
         io_info_ptr->STATS.RX_OVERFLOWS = 0;
         io_info_ptr->STATS.TX_PACKETS   = 0;
         io_info_ptr->STATS.TX_ABORTS    = 0;
         io_info_ptr->STATS.TX_UNDERFLOWS= 0;
         break;

      case IO_IOCTL_SPI_READ_WRITE:
         if (NULL == param_ptr) 
         {
            result = SPI_ERROR_INVALID_PARAMETER;
         } 
         else if (NULL != io_info_ptr->RX_BUFFER) 
         {
            result = MQX_IO_OPERATION_NOT_AVAILABLE;
         } 
         else 
         {
            rw_ptr = (SPI_READ_WRITE_STRUCT_PTR)(param_ptr);
            size = rw_ptr->BUFFER_LENGTH;
            input = (char_ptr)rw_ptr->WRITE_BUFFER;
            output = (char_ptr)rw_ptr->READ_BUFFER;            
   
            for (num = 0; num < size; num++)
            {
               val = _mpc83xx_spi_polled_tx_rx (io_info_ptr, input, TRUE, 1);
               io_info_ptr->STATS.RX_PACKETS++;
               io_info_ptr->STATS.TX_PACKETS++;
               input++;
               
 	           if (io_info_ptr->INIT.CHAR_LEN == 32) 
 	           {
 	              for (i = 0; i < 4; i++ ) 
 	              {
              	     *output++ = (char)val;
	                 val = val >> 8;		              	
 	              }
 	           	
 	           }
 	           else 
 	           {
 	              if(io_info_ptr->INIT.ENDIAN == SPI_DEVICE_BIG_ENDIAN)
 	              {	              	
                     *output++ = (char)val;
 	                 if (io_info_ptr->INIT.CHAR_LEN == 16) 
 	                 {
 	              	    *output++ = (char)(val >> 8);
 	                 }
 	              }
                  else
 	              {
 	           	     *output++ = (char)(val >> 8);
 	                 if (io_info_ptr->INIT.CHAR_LEN == 16) 
 	                 {
             	        *output++ = (char)val;	              	
 	                 }
 	              }
               }
            }
         }
         break;

      default:
         result = IO_ERROR_INVALID_IOCTL_CMD;
         break;
   } /* Endswitch */
   
   return(result);

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_tx_rx
* Returned Value   : byte received   
* Comments         : 
*   Actual transmit and receive function.
*
*END*********************************************************************/
static uint_32 _mpc83xx_spi_polled_tx_rx 
    (
        /* [IN] The address of the device registers */
        MPC83xx_SPI_INFO_STRUCT_PTR  io_info_ptr,
        
        /* [IN] Transmitted byte */
        char_ptr                   buffer,

        /* [IN] Whether to transmit */
        boolean                      tx,

        /* [IN] Number of char's to transmit */
        _mqx_int                     width
    )
{
    MPC83xx_SPI_STRUCT_PTR           spi_ptr = io_info_ptr->DEV_REG_PTR;
    uint_32                          data;
    uint_32                          index;
    uint_32                          timeout;
    
    uint_32_ptr                      buf32_ptr = (uint_32_ptr)buffer;
    uint_16_ptr                      buf16_ptr = (uint_16_ptr)buffer;
    uint_8_ptr                       buf8_ptr  = (uint_8_ptr)buffer;
    
    timeout = io_info_ptr->INIT.TIME_OUT;	  

    if (tx)
    {
        /* Assert actual chip select if SPI is a Master and Chip Selects are enabled */ 
        if ((spi_ptr->MR & MPC83xx_SPI_MR_MS) && (io_info_ptr->CS_ENABLED)) {
           io_info_ptr->CS_CALLBACK(io_info_ptr->INIT.CS, FALSE, io_info_ptr->CS_USERDATA);
        }

        /* transfer data with size and endian */ 
	    if(width == 4) {
	       spi_ptr->THR = *buf32_ptr;
	       buf32_ptr++;
	    }
	    else {
	       if(io_info_ptr->INIT.ENDIAN == SPI_DEVICE_BIG_ENDIAN) {
	          if(width == 2) {
	             spi_ptr->THR = *buf16_ptr;
	          }
	          else {
	             spi_ptr->THR = *buf8_ptr;
	          }
	       }
	       else {
	          if(width == 2) {
	             spi_ptr->THR = ((*buf16_ptr) << (16 - io_info_ptr->INIT.CHAR_LEN)) & 0x0000FFFF;
	     	  }
	     	  else {
	             spi_ptr->THR = ((*buf8_ptr) << (16 - io_info_ptr->INIT.CHAR_LEN)) & 0x0000FFFF;
	     	  }
	       }
	    }

        /* wait for write to complete */
        while (! (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NF)) { 
            if (timeout--) {
	            _time_delay(1);
    	    } else {
                return 0;
            }
        };
    }
    
    // wait read buffer not empty flag
    while (! (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_NE)) { 
        if (timeout--) {
	        _time_delay(1);
	    } else {
            return 0;
        }
    };
  
	/* Store received data in RX buffer */
	data = spi_ptr->RHR;
	
	/* Record and clear errors */
    if (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_OV) {
        io_info_ptr->STATS.RX_OVERFLOWS;
        spi_ptr->IEVENT |= MPC83xx_SPI_IEVENT_OV;
    }
    if (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_UN) {
        io_info_ptr->STATS.TX_UNDERFLOWS++;
        spi_ptr->IEVENT |= MPC83xx_SPI_IEVENT_UN;
    }
    if (spi_ptr->IEVENT & MPC83xx_SPI_IEVENT_MME) {
        io_info_ptr->STATS.TX_ABORTS++;
        spi_ptr->IEVENT |= MPC83xx_SPI_IEVENT_MME;
    }
    return data;
}



/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_rx
* Returned Value   : Returns the number of bytes received
* Comments         : 
*   Reads the data into provided array.
*
*END*********************************************************************/
static uint_32 _mpc83xx_spi_polled_rx
    (
        /* [IN] The address of the device specific information */
        IO_SPI_POLLED_DEVICE_STRUCT_PTR io_dev_ptr,
        
        /* [IN] The array the characters are written to */
        char _PTR_                      buffer,
        
        /* [IN] Number of transfers to receive */
       _mqx_int                         length
    )
{
    MPC83xx_SPI_INFO_STRUCT_PTR         io_info_ptr;
    MPC83xx_SPI_STRUCT_PTR              spi_ptr;
    uint_32                             full, width, data;
    uint_32                             num = 0;
    
    uint_32_ptr                         buf32_ptr = (uint_32_ptr)buffer;
    uint_16_ptr                         buf16_ptr = (uint_16_ptr)buffer;
    uint_8_ptr                          buf8_ptr  = (uint_8_ptr)buffer;
        
    io_info_ptr  = io_dev_ptr->DEV_INFO_PTR;
    spi_ptr = io_info_ptr->DEV_REG_PTR;

    if(io_info_ptr->INIT.CHAR_LEN == 32) {
   	   width = 4;
    }
    else if(io_info_ptr->INIT.CHAR_LEN > 8) {
   	   width = 2;
    }
    else {
   	   width = 1;
    }
   
    if(length % width) {
      /* Buffer length is not aligned with character length */
   	  return 0;
    }

    full = 1;
//    full = io_dev_ptr->FLAGS & SPI_FLAG_FULL_DUPLEX;

    while (length > 0) {
        data = _mpc83xx_spi_polled_tx_rx (io_info_ptr, buffer, (full | spi_ptr->MR & MPC83xx_SPI_MR_MS), width);
        if (0 != full) {
            io_info_ptr->STATS.TX_PACKETS++;
        }
        io_info_ptr->STATS.RX_PACKETS++;
        
        
	    if(width == 4) {
	       *buf32_ptr = data;
	    }
	    else {
	       if(io_info_ptr->INIT.ENDIAN == SPI_DEVICE_BIG_ENDIAN) {
 	          if(width == 2) {
	             *buf16_ptr = (uint_16)data;
	          }
	          else {
	             *buffer = (uint_8)data;
	          }
	       }
	       else {
	     	  data &= 0x0000FFFF;
	       	  data = data >> (16 - io_info_ptr->INIT.CHAR_LEN);
	     	  if(width == 2) {
	             *buf16_ptr = (uint_16)data;
	     	  }
	      	  else {
	             *buffer = (uint_8)data;
	     	  }
	       } 
	    }
        buffer = buffer + width;
    	length -= width;
	    num+= width;
    }    
    return num;
}

/*FUNCTION****************************************************************
* 
* Function Name    : _mpc83xx_spi_polled_tx
* Returned Value   : return number of byte transmitted
* Comments         : 
*   Writes the provided data buffer and loops until transmission complete
*
*END*********************************************************************/
static uint_32 _mpc83xx_spi_polled_tx
    (
        /* [IN] The address of the device specific information */
        IO_SPI_POLLED_DEVICE_STRUCT_PTR io_dev_ptr,
        
        /* [IN] The array to store data */
        char _PTR_                      buffer,
        
        /* [IN] Number of char's to transmit */
        _mqx_int                        length
    )
{
    MPC83xx_SPI_INFO_STRUCT_PTR         io_info_ptr;
    MPC83xx_SPI_STRUCT_PTR              spi_ptr;
    uint_32                             width;
    uint_32                             num = 0;
    uint_32                             data;
    
    io_info_ptr  = io_dev_ptr->DEV_INFO_PTR;
    spi_ptr = io_info_ptr->DEV_REG_PTR;

    if(io_info_ptr->INIT.CHAR_LEN == 32) {
   	   width = 4;
    }
    else if(io_info_ptr->INIT.CHAR_LEN > 8) {
   	   width = 2;
    }
    else {
   	   width = 1;
    }
   
    if(length % width) {
      /* Buffer length is not aligned with character length */
   	  return 0;
    }

    while (length > 0) {
        data = _mpc83xx_spi_polled_tx_rx (io_info_ptr, buffer, TRUE, width);
        io_info_ptr->STATS.TX_PACKETS++;
        buffer = buffer + width;
    	length -= width;
	    num+= width;
    }
    return num;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _bsp_spi_set_cs
*  Returned Value : MQX_OK or error code
*  Comments       : Sets SPI chip select high
*       
*
*END*-----------------------------------------------------------------*/

uint_32 _bsp_spi_set_cs
   (
   )
{ /* Body */
    _bsp_gpio_set_data(MPC8349_GPIO_PORT1, 8, TRUE);
    return MQX_OK;
} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _bsp_spi_clear_cs
*  Returned Value : MQX_OK or error code
*  Comments       : Sets SPI chip select low
*       
*
*END*-----------------------------------------------------------------*/

uint_32 _bsp_spi_clear_cs 
   (
   )
{
         _bsp_gpio_set_data(MPC8349_GPIO_PORT1, 8, FALSE);
    return MQX_OK;
} 

/* EOF */
